﻿/*
<File>
	<Copyright>Copyright © 2007, Daniel Vaughan. All rights reserved.</Copyright>
	<License see="//License.txt"/>
	<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
	<CreationDate>2007/11/27 12:44</CreationDate>
	<LastSubmissionDate>$Date: $</LastSubmissionDate>
	<Version>$Revision: $</Version>
</File>
*/

using System;
using System.Deployment.Application;

using DanielVaughan.Logging.LogEntries;
using DanielVaughan.ServiceModel;

namespace DanielVaughan.Logging.LogStrategies
{
	sealed class ClientStrategy : IClientStrategy
	{
		ClientConfigurationData configData;
		readonly object configLock = new object();

		ClientConfigurationData GetConfigurationData(IClientInfo clientInfo)
		{
			if (configData == null || configData.RetrievedOn.AddSeconds(configData.ExpiresInSeconds) < DateTime.Now)
			{
				lock (configLock)
				{
					if (configData == null || configData.RetrievedOn.AddSeconds(configData.ExpiresInSeconds) < DateTime.Now)
					{
						/* We convert the specified IClientInfo to a new ClientInfo,
						 thus preventing serialization failures with custom log entries. */
						var clogService = ChannelManagerSingleton.Instance.GetChannel<IClogService>();
						configData = clogService.GetConfiguration(clientInfo);
						configData.RetrievedOn = DateTime.Now;
					}
				}
			}
			return configData;
		}

		public LogLevel GetLogLevel(IClientInfo clientInfo)
		{
			var info = new ClientInfo(clientInfo);

			if (ApplicationDeployment.IsNetworkDeployed)
			{
				Uri launchUri = ApplicationDeployment.CurrentDeployment.ActivationUri;
				info.Url = launchUri.ToString();
				info.LogName = GetName(clientInfo.LogName, launchUri.ToString());
			}

			var clientConfigurationData = GetConfigurationData(info);

			return clientConfigurationData.LogLevel;
		}

		public void Write(IClientLogEntry logEntry)
		{
			throw new InvalidOperationException(
				"This strategy is only to be deployed in a remote client.");
		}

		public void Write(IServerLogEntry logEntry)
		{
			var clientInfo = new ClientInfo(logEntry);
			ClientConfigurationData configurationData = GetConfigurationData(clientInfo);
			if (configurationData == null || !configurationData.LogEnabled 
				|| (int)configurationData.LogLevel > (int)logEntry.LogLevel)
			{
				return;
			}

			var data = new LogEntryData(logEntry);
			data.UserName = SafeEnvironmentValues.UserName;
			data.MachineName = SafeEnvironmentValues.MachineName;

			if (logEntry.Exception != null)
			{
				data.ExceptionMemento = new ExceptionMemento(logEntry.Exception);
			}

			if (ApplicationDeployment.IsNetworkDeployed)
			{
				Uri launchUri = ApplicationDeployment.CurrentDeployment.ActivationUri;
				if (launchUri != null)
				{
					data.Url = launchUri.ToString();
				}
				data.LogName = GetName(logEntry.CodeLocation.ClassName, data.Url);
			}	
			else
			{
				data.LogName = logEntry.CodeLocation.ClassName;
			}

			var clogService = ChannelManagerSingleton.Instance.GetChannel<IClogService>();
			clogService.WriteEntry(data);
		}

		static string GetName(Type hostType, string url)
		{
			return string.Format("{0},{1}", hostType.FullName, GetUrlForLogName(url));
		}

		static string GetName(string className, string url)
		{
			return string.Format("{0},{1}", className, GetUrlForLogName(url));
		}

		static string GetName(Type hostType)
		{
			return hostType.FullName;
		}

		/// <summary>
		/// Gets the name of the URL for the log data.
		/// If we are logging from localhost then we do 
		/// not use the port number, because it is assumed
		/// that we are debugging.
		/// </summary>
		/// <returns>The url of the page without the querystring.</returns>
		static string GetUrlForLogName(string pageUrl)
		{
			string result;

			Uri uri = new Uri(pageUrl);
			if (uri.Host.ToLower() == "localhost" || uri.Host.Contains("127.0.0."))
			{
				result = string.Format("{0}", uri.LocalPath);
			}
			else
			{
				if (uri.Port == 80 || uri.Port == 443)
				{
					result = string.Format("{0}://{1}{2}", uri.Scheme, uri.Host, uri.LocalPath);
				}
				else
				{
					result = string.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Host, uri.Port, uri.LocalPath);
				}
			}

			return result.ToLower();
		}
	}
}
